
/*
 * @项目名称: 模板开发
 * @FilePath: /meek/src/geometry/support/cubeutil.js
 * @Author: zhangning@datatang.com
 * @Date: 2019-11-14T17:59:02+08:00
 * @LastEditors: yangxianghong@datatang.com
 * @LastEditTime: 2023-07-12 10:50:08
 * Copyright (c) 2018-2022 
 */
                                                    
/**
 */

import Polygon from "./../polygon";
import { squaredSegmentDistance, distance } from "./geometryutil";
import Cube from "../cube";
import { ContextReplacementPlugin } from "webpack";

const DEFAULT_DEEP = 50;

export const CubeUtil = {}

CubeUtil.modificationHandlers = {
  1: {
    "front": function (coordinates, vertex, segmentData, extent) {
      // 修改第一面坐标
      const firstFace = deepClone(coordinates[0][0])
      const newCoordinates = CubeUtil.updateExtent(
        firstFace,
        vertex,
        segmentData
      )

      // 限制左右/上下边不能交换
      if (
        newCoordinates[3][0] <= newCoordinates[0][0] ||
        newCoordinates[0][1] <= newCoordinates[1][1]
      ) {
        return false;
      }


      // 如果有第二面，则同时更新第二面的坐标
      if (coordinates[1]) {
        const secondFace = deepClone(coordinates[1][0]);
        const newSecondFace = CubeUtil.updateSecondFace(
          secondFace,
          newCoordinates,
          this.isLeft,
          extent
        );

        if (!newSecondFace) {
          return false;
        }

        coordinates[0][0] = newCoordinates;
        coordinates[1][0] = newSecondFace;
      } else {
        coordinates[0][0] = newCoordinates;
      }

      return true;
    },
    "back": function() {
      return true;
    },
    "getCubeEightVertexs": function(coordinates, isLeft) {
      return CubeUtil.getPointsFromFirstAndSecondFace(coordinates, isLeft);
    }
  },
  2: {
    "front": function (coordinates, vertex, segmentData) {
      const firstFace = deepClone(coordinates[0][0])
      const newCoordinates = CubeUtil.updateExtent(
        firstFace,
        vertex,
        segmentData
      )

      // 限制上下左右不能交换
      if (
        newCoordinates[3][0] <= newCoordinates[0][0] ||
        newCoordinates[0][1] <= newCoordinates[1][1]
      ) {
        return false;
      }

      if (coordinates[1]) {
        const secondFace = deepClone(coordinates[1][0]);
        if (this.isLeft) {
          secondFace[0] = newCoordinates[0]
          secondFace[1] = newCoordinates[1]
        } else {
          secondFace[0] = newCoordinates[3]
          secondFace[1] = newCoordinates[2]
        }
        coordinates[1][0] = secondFace
      }

      coordinates[0][0] = newCoordinates;
      return true;
    },
    "back": function(coordinates, vertex, segmentData) {
      let dragSegment = {};
      dragSegment.isVertex = segmentData.isVertex;

      // 根据鼠标拖动的点（或边）的状态，映射到后面对应的index上
      dragSegment.index = CubeUtil.getDragIndexOfBackFace(segmentData, this.isLeft);
      if (dragSegment.index === false) {
        return;
      }

      const backFace = deepClone(coordinates[3][0])
      const newCoordinates = CubeUtil.updateExtent(
        backFace,
        vertex,
        dragSegment
      )

      // 限制上下左右不能交换
      if (
        newCoordinates[3][0] >= newCoordinates[0][0] ||
        newCoordinates[0][1] <= newCoordinates[1][1]
      ) {
        return false;
      }

      coordinates[3][0] = newCoordinates
      return true;
    },
    "getCubeEightVertexs": function(coordinates, isLeft) {
      return CubeUtil.getPointsFromFrontAndBackFace(coordinates, isLeft);
    }
  },
  3: {
    "front": function(coordinates, vertex, segmentData, extent, shiftKey) {
      // 兼容报错
      if(coordinates.length < 4) {
        return false;
      }
      const {polygonIndex, index, isVertex} = segmentData;

      const firstFace = deepClone(coordinates[0][0]);
      const backFace = deepClone(coordinates[3][0]);

      CubeUtil.updateTrapezium(firstFace, index, isVertex, vertex, backFace, shiftKey);

      if (
        // 左右边不允许交换
        firstFace[0][0] >= firstFace[3][0] ||

        //上下点不允许交换
        firstFace[1][1] >= firstFace[0][1] ||
        firstFace[2][1] >= firstFace[3][1]
      ) {
        return false;
      }

      coordinates[0][0] = firstFace;
      coordinates[3][0] = backFace;
      return true;
    },
    "back": function(coordinates, vertex, segmentData, extent, shiftKey) {
      const index = CubeUtil.getDragIndexOfBackFace(segmentData, this.isLeft);
      if(coordinates.length < 4) {
        return false;
      }
      const firstFace = deepClone(coordinates[0][0]);
      const backFace = deepClone(coordinates[3][0]);

      CubeUtil.updateTrapezium(backFace, index, segmentData.isVertex, vertex, firstFace,shiftKey);

      if (
        // 左右边不允许交换
        backFace[0][0] <= backFace[3][0] ||

        //上下点不允许交换
        backFace[1][1] >= backFace[0][1] ||
        backFace[2][1] >= backFace[3][1]
      ) {
        return false;
      }

      coordinates[0][0] = firstFace;
      coordinates[3][0] = backFace;
      return true;
    },
    "getCubeEightVertexs": function(coordinates, isLeft) {
      return CubeUtil.getPointsFromFrontAndBackFace(coordinates, isLeft);
    }
  },
  ctrl: function(segmentData, coordinates, vertex) {
    const { polygonIndex, index } = segmentData;
    if (index !== 0 && index !== 2) {
      return;
    }

    const firstRect = coordinates[0][0];
    if (
      polygonIndex !== 0
    ) {
      return;
    }

    if (
      index === 0 &&
      vertex[0] >= firstRect[0][0]
    ) {
      return;
    }

    if (
      index === 2 &&
      vertex[0] <= firstRect[3][0]
    ) {
      return;
    }

    // 记录当前的立方体是否由左侧拖出
    this.isLeft = ( index === 0 )

    coordinates[1] = [];
    coordinates[1][0] = CubeUtil.createSecondFace(
      firstRect,
      vertex,
      this.isLeft
    );
  }
}

CubeUtil.getDragIndexOfBackFace = function(segmentData, isLeft) {
  const {polygonIndex, index, isVertex} = segmentData;

  let dragSegment = {};

  if (isLeft) {
    if ( polygonIndex === 3 && index === 0 ) {
      dragSegment.index = 0
    } else if (
      ( isVertex && polygonIndex === 2 && index === 3 ) ||
      ( !isVertex && polygonIndex === 2 && index === 2 )
    ) {
      dragSegment.index = 1
    } else if ( polygonIndex === 1 && index === 2) {
      dragSegment.index = 2
    } else if (
      ( isVertex && polygonIndex === 1 && index === 3 ) ||
      ( !isVertex && polygonIndex === 3 && index === 3 )
    ) {
      dragSegment.index = 3
    } else {
      return false;
    }
  } else {
    if (
      (  isVertex && polygonIndex === 1 && index === 3 ) ||
      ( !isVertex && polygonIndex === 1 && index === 2 )
      ) {
      dragSegment.index = 0
    } else if (
      ( isVertex && polygonIndex === 1 && index === 2 ) ||
      ( !isVertex && polygonIndex === 2 && index === 2 )
    ) {
      dragSegment.index = 1
    } else if ( 
      ( isVertex && polygonIndex === 2 && index === 2 ) ||
      ( !isVertex && polygonIndex === 3 && index === 2 )
    ) {
      dragSegment.index = 2
    } else if (polygonIndex === 3 && index === 3 ) {
      dragSegment.index = 3
    } else {
      return false;
    }
  }

  return dragSegment.index;
}

CubeUtil.updateTrapezium = function(face, index, isVertex, vertex,  otherFace, shiftKey) {
  if (isVertex) {
    if(shiftKey) {
      const i = 4 - index - 1;
      const _vertex = deepClone(face[index]);
      const dx = _vertex[0] - vertex[0];
      const dy = _vertex[1] - vertex[1];
      otherFace[i] = [otherFace[i][0] - dx, otherFace[i][1] - dy];
      const index1 = this.getAdjacentYIndex(i);
      otherFace[index1][0] = otherFace[index1][0] - dx;

    }
    face[index] = vertex;
    const index1 = this.getAdjacentYIndex(index);
    face[index1][0] = vertex[0];
  } else {
    //return;
    // 上下边不允许调整
    if (index === false || index % 2 === 1) {
      return;
    }

    if (index < 1) {
      face[0][0] = vertex[0];
      face[1][0] = vertex[0];
    } else {
      face[3][0] = vertex[0];
      face[2][0] = vertex[0];
    }
  }
}

/***
 * 获取点下标在y轴上的对应点
 * 0 - 1
 * 2 - 3
 */
CubeUtil.getAdjacentYIndex = function(index) {
  if (index >= 2) {
    return (index - 2) ^ 1 + 2;
  } else {
    return index ^ 1;
  }
}

/**
 * 已知首面和侧面的坐标，求八点坐标
 * @param {Array} coordinates 立方体坐标
 * @param {Boolean} isLeft 立方体是否为从左侧拖出
 */
CubeUtil.getPointsFromFirstAndSecondFace = function(coordinates, isLeft) {
  let vertexes = []
  if (isLeft) {
    const v0 = coordinates[0][0][0];
    const v1 = coordinates[0][0][3];
    const v2 = coordinates[0][0][2];
    const v3 = coordinates[0][0][1];
    const v4 = coordinates[1][0][2];
    const v5 = coordinates[1][0][3];
    const v6x = v1[0] - (v0[0] - v5[0]);
    const v6 = [v6x, v4[1]];
    const v7 = [v6[0], v5[1]];
    vertexes = [v0, v1, v2, v3, v4, v5, v6, v7];
  } else {
    const v0 = coordinates[0][0][0];
    const v1 = coordinates[0][0][3];
    const v2 = coordinates[0][0][2];
    const v3 = coordinates[0][0][1];
    const v6 = coordinates[1][0][2];
    const v7 = coordinates[1][0][3];
    const v4x = (v7[0] - v1[0] + v0[0]);
    const v4 = [v4x, v6[1]];
    const v5 = [v4[0], v7[1]];
    vertexes = [v0, v1, v2, v3, v4, v5, v6, v7];
  }
  return vertexes
}

/**
 * 已知首面和背面的坐标，求八点坐标
 * param {Array} coordinates 立方体坐标
 * @param {Boolean} isLeft 立方体是否为从左侧拖出
*/
CubeUtil.getPointsFromFrontAndBackFace = function(coordinates, isLeft) {
  let vertexes = []
  if (isLeft) {
    const v0 = coordinates[0][0][0];
    const v1 = coordinates[0][0][3];
    const v2 = coordinates[0][0][2];
    const v3 = coordinates[0][0][1];
    const v4 = coordinates[3][0][2];
    const v5 = coordinates[3][0][3];
    const v6 = coordinates[3][0][1];
    const v7 = coordinates[3][0][0];
    vertexes = [v0, v1, v2, v3, v4, v5, v6, v7];
  } else {
    const v0 = coordinates[0][0][0];
    const v1 = coordinates[0][0][3];
    const v2 = coordinates[0][0][2];
    const v3 = coordinates[0][0][1];
    const v4 = coordinates[3][0][2]
    const v5 = coordinates[3][0][3]
    const v6 = coordinates[3][0][1];
    const v7 = coordinates[3][0][0];
    vertexes = [v0, v1, v2, v3, v4, v5, v6, v7];
  }
  return vertexes;
}

/**
 * 矩形坐标点更新
 *
 * @method updateExtent
 * @param geometry {Geometry}
 * @param newCoordinates {Array}
 * @param dragSegments
 * @returns {Array}
 */
CubeUtil.updateExtent = function (extentCoordinates, newCoordinates, dragSegment) {
  const index = dragSegment.index
  let nextIndex = index < 3 ? index + 1 : 0
  const segment1 = extentCoordinates[index]
  const segment2 = extentCoordinates[nextIndex]
  
  const isVertex = dragSegment.isVertex
  if (isVertex) {
    if (index === 0) {
      extentCoordinates[0] = newCoordinates
      extentCoordinates[3][1] = newCoordinates[1]
      extentCoordinates[1][0] = newCoordinates[0]
    } else if (index === 1) {
      extentCoordinates[1] = newCoordinates
      extentCoordinates[0][0] = newCoordinates[0]
      extentCoordinates[2][1] = newCoordinates[1]
    } else if (index === 2) {
      extentCoordinates[2] = newCoordinates
      extentCoordinates[3][0] = newCoordinates[0]
      extentCoordinates[1][1] = newCoordinates[1]
    } else if (index === 3) {
      extentCoordinates[3] = newCoordinates
      extentCoordinates[0][1] = newCoordinates[1]
      extentCoordinates[2][0] = newCoordinates[0]
    }
  } else {
    if (segment1[0] === segment2[0]) {
      extentCoordinates[index][0] = newCoordinates[0]
      extentCoordinates[nextIndex][0] = newCoordinates[0]
    }
  
    if (segment1[1] === segment2[1]) {
      extentCoordinates[index][1] = newCoordinates[1]
      extentCoordinates[nextIndex][1] = newCoordinates[1]
    }
  }

  return extentCoordinates
}

/**
 * 移动矩形
 * 
 * @param {Array} coordinates 数组
 * @param {Number} x x轴偏移量
 * @param {Number} y y轴偏移量
 * @return {Array}
 */
CubeUtil.moveExtent = function (coordinates, x, y) {
  const newCoords = []
  for (let coord of coordinates) {
    newCoords.push([coord[0] + x, coord[1] + y]);
  }
  return newCoords;
}

/**
 * 判断点是否在矩形上
 *
 * @method containsPoint
 * @param {Array} coords
 * @param {Number} x
 * @param {Number} y
 * @returns {Boolean}
 */
CubeUtil.containsPoint = function(coords, x, y){
  return x >= coords[0][0] &&
    x <= coords[3][0] &&
    y >= coords[1][1] &&
    y <= coords[0][1]; 
}

/**
 * 根据八个点的坐标，更新六个面坐标
 */
CubeUtil.updateCoordinatesFromPoints = function(coordinates, points, isLeft, filteredIndex, extent) {
  if (extent) {
    const [xMin, yMin, xMax, yMax] = extent;
    for (let p of points) {
      if (
        p[0] < xMin ||
        p[1] < yMin ||
        p[0] > xMax ||
        p[1] > yMax
      ) {
        return;
      }
    }
  }


  // 立方体正面
  if (filteredIndex !== 0) {
    coordinates[0] = [[]]
    coordinates[0][0] = [points[0], points[3], points[2], points[1]]
  }

  // 拉出立方体的侧面
  coordinates[1] = [[]];
  if (isLeft) {
    coordinates[1][0] = [points[0], points[3], points[4], points[5]]
  } else {
    coordinates[1][0] = [points[1], points[2], points[6], points[7]]
  }

  // 顶面
  coordinates[2] = [[]];
  coordinates[2][0] = [points[2], points[3], points[4], points[6]]

  // 后面（正对着高亮的矩形的那一面）
  coordinates[3] = [[]];
  coordinates[3][0] = [points[7], points[6], points[4], points[5]]

  // 与拉出的面相对的面
  coordinates[4] = [[]];
  if (isLeft) {
    coordinates[4][0] = [points[1], points[2], points[6], points[7]]
  } else {
    coordinates[4][0] = [points[0], points[3], points[4], points[5]]
  }

  // 底面
  coordinates[5] = [[]];
  coordinates[5][0] = [points[0], points[5], points[7], points[1]]
  return true;
}

/**
 * （标准立方体）修改首面时修改侧面坐标
 * @param {Array} secondFace 原侧面坐标
 * @param {Array} newCoordinates 更新后的首面坐标
 * @param {Boolean} isLeft 立方体是否由左侧拉出
 * @param {Array} extent （可选）画布限制
 */
CubeUtil.updateSecondFace = function(secondFace, newCoordinates, isLeft, extent) {
  if (!isLeft) {
    // 从右侧拖出
    const deep = secondFace[2][1] - secondFace[1][1];
    const width = secondFace[2][0] - secondFace[1][0];

    secondFace[0] = newCoordinates[3]
    secondFace[1] = newCoordinates[2]
    secondFace[3] = [newCoordinates[3][0] + width, newCoordinates[3][1] + deep]
    secondFace[2] = [newCoordinates[2][0] + width, newCoordinates[2][1] + deep]

    if (
      //secondFace[3][0] <= newCoordinates[3][0] ||
      (extent && secondFace[3][0] >= extent[2])
    ) {
      return;
    }

  } else {
    const deep = secondFace[2][1] - secondFace[1][1]
    const width = secondFace[1][0] - secondFace[2][0]

    secondFace[0] = newCoordinates[0]
    secondFace[1] = newCoordinates[1]
    secondFace[2] = [newCoordinates[1][0] - width, newCoordinates[1][1] + deep]
    secondFace[3] = [newCoordinates[0][0] - width, newCoordinates[0][1] + deep]

    if (
      //secondFace[3][0] >= newCoordinates[0][0] ||
      (extent && secondFace[3][0] <= 0)
    ) {
      return;
    }
  }
  return secondFace;
}

/**
 * 根据默认深度和鼠标拖动位置计算侧面坐标
 * @param {Array} firstFace 首面坐标
 * @param {Array} vertex 当前鼠标所在x和y坐标
 * @param {Boolean} isLeft 是否由左侧拖出
 */
CubeUtil.createSecondFace = function(firstFace, vertex, isLeft) {
  let newPolygon = [];
  
  let targetLine1;
  let targetLine2;

  if (isLeft ){
    targetLine1 = firstFace[0]
    targetLine2 = firstFace[1]
  } else {
    targetLine1 = firstFace[3]
    targetLine2 = firstFace[2]
  }

  newPolygon[0] = targetLine1
  newPolygon[1] = targetLine2

  let y2;
  let y3;
  if (firstFace[1][1] - DEFAULT_DEEP < 0) {
    y2 = 0;
    y3 = targetLine1[1] - targetLine2[1]
  } else {
    y2 = targetLine2[1] - DEFAULT_DEEP;
    y3 = targetLine1[1] - DEFAULT_DEEP;
  }

  const x = vertex[0];
  newPolygon[2] = [x, y2];
  newPolygon[3] = [x, y3];
  return newPolygon;
}


/**
 * 创建或更新矩形
 *
 * @method createOrUpdate
 * @param minX {Number}
 * @param minY {Number}
 * @param maxX {Number}
 * @param maxY {Number}
 * @param opt_extent {Array}
 * @returns {*}
 */
CubeUtil.createOrUpdate = function (minX, minY, maxX, maxY, opt_extent) {
  if (opt_extent) {
    opt_extent[0] = minX
    opt_extent[1] = minY
    opt_extent[2] = maxX
    opt_extent[3] = maxY
    return opt_extent
  } else {
    return [minX, minY, maxX, maxY]
  }
}

/**
 * 通过坐标数据构建矩形
 *
 * @method boundingExtent
 * @param extent
 * @param coordinate
 */
CubeUtil.boundingCube = function (coordinates) {
  const xCoords = coordinates.flat(Infinity).filter((_, i) => {
    return i % 2 == 0;
  });
  const yCoords = coordinates.flat(Infinity).filter((_, i) => {
    return i % 2 !== 0;
  })
  const firstPoint = [Math.min(...xCoords), Math.min(...yCoords)];
  const lastPoint = [Math.max(...xCoords), Math.max(...yCoords)];
  let points = [
    [firstPoint[0], lastPoint[1]],
    [lastPoint[0], lastPoint[1]],
    [lastPoint[0], firstPoint[1]],
    [firstPoint[0], firstPoint[1]],
    [firstPoint[0], lastPoint[1]],
    [lastPoint[0], lastPoint[1]],
    [lastPoint[0], firstPoint[1]],
    [firstPoint[0], firstPoint[1]],
  ]
  let coordinates1 = [
    // [[points[4], points[5], points[7], points[6]]], //后
    // [[points[0], points[4], points[6], points[1]]], //下
    // [[points[0], points[3], points[5], points[4]]], //左
    // [[points[1], points[2], points[7], points[6]]], //右
    // [[points[2], points[3], points[5], points[7]]], //上
    [[points[0], points[3], points[2], points[1]]], //前
  ]

  return coordinates1

}

/**
 * Build an extent that includes all given coordinates.
 * @param extent
 * @param coordinate
 */
CubeUtil.boundingSimpleCUBE = function (coordinates) {
  let extent = ExtentUtil.createEmpty()

  for (let i = 0, ii = coordinates.length; i < ii; ++i) {
    ExtentUtil.extendCoordinate(extent, coordinates[i])
  }

  return extent
}

/**
 * 通过坐标更新矩形
 *
 * @method extendCoordinate
 * @param extent {Geometry}
 * @param coordinate {Array}
 */
CubeUtil.cubeCoordinate = function (cube, coordinate) {
  if (coordinate[0] < extent[0]) {
    extent[0] = coordinate[0]
  }

  if (coordinate[0] > extent[2]) {
    extent[2] = coordinate[0]
  }

  if (coordinate[1] < extent[1]) {
    extent[1] = coordinate[1]
  }

  if (coordinate[1] > extent[3]) {
    extent[3] = coordinate[1]
  }
}

/**
 * Convert xmin, ymin, xmax, ymax of an extent to a ring array
 * @param xmin
 * @param ymin
 * @param xmax
 * @param ymax
 * @returns {[*,*,*,*,*]}
 */
CubeUtil.minMaxToRing = function (xmin, ymin, xmax, ymax) {
  const ring = [
    [xmin, ymin],
    [xmax, ymin],
    [xmax, ymax],
    [xmin, ymax],
    [xmin, ymin]
  ]

  return ring
}

/**
 * 判断面是否可见
 * @param {Cube} geometry 3d拉框对象
 * @param {Number} index 面的序列数字
 */
CubeUtil.isFaceVisible = function(geometry, index, face) {
  if (index < 1 || index > geometry.rings.length) {
    return true;
  }

  const coordinates = geometry.rings;
  const currentFace = coordinates[index][0];

  if (index === 3) {
    return false;
  } else {
    const firstFace = coordinates[0][0]
    const angle = getAngleOfFace(currentFace, firstFace, face);
    return angle > 90
    return !isFaceIntersectWithFace(currentFace, firstFace) &&
      !isFaceIntersectWithFace(firstFace, currentFace);
  }
}

/**
 * 获取3d拉框对应面对面的描述
 */
CubeUtil.getDescriptionOfFace = function(geometry, index) {
  if (index === 0) {
    return "back";
  } else if (index === 3) {
    return "front";
  } else if (index === 2) {
    return "top"
  } else if (index === 5) {
    return "bottom"
  } else if (index === 1) {
    if (geometry.isLeft) {
      return "left"
    } else {
      return "right"
    }
  } else {
    if (geometry.isLeft) {
      return "right"
    } else {
      return "left"
    }
  }
}

/**
 * 计算面的面积
 * @param {Array} face
 * @return {Number}
 */
CubeUtil.getAreaOfFace = function(face) {
  if (face.length < 4) {
    return;
  }

  const [p1, p2, p3, p4] = face;

  // 任意选取四边形的一个对角线作为底
  const A = distance(p1, p3);

  // 计算其他两个点到对角线的距离
  const squaredDis1 = squaredSegmentDistance(p2[0], p2[1], p1[0], p1[1], p3[0], p3[1])
  const squaredDis2 = squaredSegmentDistance(p4[0], p4[1],  p1[0], p1[1], p3[0], p3[1])
  const h1 = Math.sqrt(squaredDis1)
  const h2 = Math.sqrt(squaredDis2)

  return A * h1 / 2 + A * h2 / 2;
}

/**
 * 判断面与面是否有交集
 * @param {Array} evaluated
 * @param {Array} target 
 */
function isFaceIntersectWithFace(evaluated, target) {

  return evaluated.some((point, index) => {
    let nextP = evaluated[index + 1]
    if (!nextP) {
      nextP = evaluated[0]
    }

    return CubeUtil.isEdgeIntersectWidthFace([point, nextP], target);
  })
}

/**
 * 获取两个面相交的角度
 * @param {Array} evaluated 需要判断的面
 * @param {Array} target 基准面
 * @param {String} face 需要判断的面的描述
 * @return {Number} 角度
 */
export function getAngleOfFace(evaluated, target, face) {
  return CubeUtil.getAngleOfFace[face](evaluated, target);
}

/**
 * 根据不同的面，获取与矩形面计算角度的向量
 */
CubeUtil.getAngleOfFace = {
  "left": (evaluated, target) => {
    return getAngle(evaluated[1], evaluated[2], target[2])
  },
  "top": (evaluated, target) => {
    return getAngle(evaluated[1], evaluated[2], target[0])
  },
  "right": (evaluated, target) => {
    return getAngle(evaluated[1], evaluated[2], target[1])
  },
  "bottom": (evaluated, target) => {
    return getAngle(evaluated[0], evaluated[1], target[1])
  }
}

/**
 * 计算向量 p -> p1 与 p -> p2 之间的夹角
 * @param {Array} p 两条线段的交点
 * @param {Array} p1
 * @param {Array} p2
 * @return {Number} 角度
 */
function getAngle(p, p1, p2) {
  let angle = 0;

  // 向量Vector a的(x, y)坐标
  let va_x = p1[0] - p[0];
  let va_y = p1[1] - p[1];

  // 向量向量Vector b的(x, y)坐标
  let vb_x = p2[0] - p[0];
  let vb_y = p2[1] - p[1];

  let productValue = (va_x * vb_x) + (va_y * vb_y);
  let va_val = Math.sqrt(va_x * va_x + va_y * va_y);  // 向量a的模
  let vb_val = Math.sqrt(vb_x * vb_x + vb_y * vb_y);  // 向量b的模
  let cosValue = productValue / (va_val * vb_val); // 余弦公式

  if (cosValue < -1 && cosValue > -2) {
    cosValue = -1;
  } else if (cosValue > 1 && cosValue < 2) {
    cosValue = 1;
  }

  angle = Math.acos(cosValue) * 180 / Math.PI; 
  return angle;
}

/**
 * 判断边是否与面相交
 * @param {Array} edge 
 * @param {Array} target 
 */
CubeUtil.isEdgeIntersectWidthFace = function(edge, face) {
  return !CubeUtil.isLineTheEdgeOfTheFace(edge, face)
    && (
      isAnyPointOfALineInTheFace(edge, face) ||
      isLineIntersectWithFace(face, edge)
    )
}

/**
 * 判断线段的两个定点是否有一个顶点在面内
 * @param {Array} line 
 * @param {Array} face 
 */
function isAnyPointOfALineInTheFace(line, face) {
  return line.some(point => {
    return isPointInFace(point, face);
  })
}

/**
 * 判断线是否与面的某一边完全重合
 * @param {Array} line
 * @param {Array} face 
 */
CubeUtil.isLineTheEdgeOfTheFace = function(line, face) {
  return face.some((point1, index) => {
    let point2 = face[index + 1];
    if (!point2) {
      point2 = face[0]
    }

    return isLineEqualToLine([point1, point2], line) ||
      isLineEqualToLine([point2, point1], line)
  })
}

CubeUtil.getIntersectedLinesOfLineWithFace = function(line, face, i) {
  const evaluatedP = face[i];
  const nextP = face[i + 1] == null ? face[0] : face[i + 1];
  const point = getIntersectionOfLines(line[0], line[1], evaluatedP, nextP);

  // 没有相交点，即不存在半实半虚的情况，走从前的处理逻辑即可
  if (!point) {
    return;
  }

  point[0] = (point[0] + 0.5) | 0
  point[1] = (point[1] + 0.5) | 0
  
  // 找到相交点之后，需要把line截成两段
  const lines = [];
  line.forEach((p, ii) => {
    const halfLine = [point, p];
    p[0] = (p[0] + 0.5) | 0
    p[1] = (p[1] + 0.5) | 0
    // 判断这半段线虚实
    let visible = true;
    // p点在矩形面，虚线
    if (isPointInFace(p, face)) {
      visible = false;
    }

    // 线段与其他边相交，虚线
    let isIntersected = face.some((pp, j) => {
      if (i === j) {
        return false;
      }

      const nextP = face[j + 1] == null ? face[0] : face[j + 1];
      const intersected = getIntersectionOfLines(point, p, pp, nextP)
      return intersected &&
        // 相交点不能是线段的顶点
        !isPointTheEdgeOfLine(intersected, halfLine);
    })

    if (isIntersected) {
      visible = false;
    }

    lines.push({
      coord: [point, p],
      visible
    })
  })

  return lines;
}

/**
 * 判断点是否为面的顶点
 * @param {Array} point
 * @param {Array} face
 */
function isPointEdgeOfTheFace(point, face) {
  return face.some((p1, index) => {
    let p2 = face[index + 1];
    if (!p2) {
      p2 = face[0]
    }

    return isPointTheEdgeOfLine(point, [p1, p2]);
  });
}

/**
 * 判断两条线段是否重合（通过判断顶点是否完全相等）
 * @param {Array} line1 
 * @param {Array} line2
 * @return {Boolean}
 */
function isLineEqualToLine(line1, line2) {
  return line1.every((p1) => {
    return isPointTheEdgeOfLine(p1, line2);
  })
}

/**
 * 判断点是否为线段的顶点
 * @param {Array} point 
 * @param {Array} line
 * @return {Boolean}
 */
function isPointTheEdgeOfLine(point, line) {
  // 浮点数计算出的会有误差，需要有一定的误差性
  return line.some((p) => {
    return Math.abs(point[0] - p[0]) < 0.000001
      && Math.abs(point[1] - p[1]) < 0.000001
  });
}

/**
 * 判断线是否与面相交
 * @param {Array} face 
 * @param {Array} line
 * @return {Boolean} 
 */
function isLineIntersectWithFace(face, line) {
  const [point1, point2] = line;
  for(let i = 0, len = face.length; i < len; i++) {
    let nextI = i + 1 > len - 1 ? 0 : i + 1
    const current = face[i]
    const next = face[nextI]

    const result = getIntersectionOfLines(point1, point2, current, next)
    if (result) {
      if (isPointEdgeOfTheFace(result, face)) {
        continue;
      } else {
        return true;
      }
    }
  }
}

/**
 * 求AB两条线段的相交点
 *
 * @method getIntersectionOfLines
 * @param {Array} a 线段A的第一个坐标
 * @param {Array} b 线段A的第二个坐标
 * @param {Array} c 线段B的第一个坐标
 * @param {Array} d 线段B的第二个坐标
 * @return {Array|Boolean} 如相交，返回坐标;如不想交，返回false
*/
function getIntersectionOfLines(a, b, c, d) {
  const x11_21 = a[0] - c[0]
  const y11_21 = a[1] - c[1]
  const x12_11 = b[0] - a[0]
  const y12_11 = b[1] - a[1]
  const y22_21 = d[1] - c[1]
  const x22_21 = d[0] - c[0]
  const dis = (y22_21 * x12_11) - (x22_21 * y12_11)
  const n1 = (x22_21 * y11_21) - (y22_21 * x11_21)
  const n2 = (x12_11 * y11_21) - (y12_11 * x11_21)

  if (dis == 0) {
    // parallel
    if (n1 == 0 && n2 == 0) {
      // coincident
      return false
    }
  } else {
    const along1 = n1 / dis
    const along2 = n2 / dis
    
    if (along1 >= 0 && along1 <= 1 && along2 >=0 && along2 <= 1) {
      // intersect
      // calculate the intersection point
      const x = a[0] + (along1 * x12_11)
      const y = a[1] + (along1 * y12_11)
      return [x, y]
    }
  }
}

/**
 * 判断某一点是否在面内
 * @param {Array} point 
 * @param {Array} face
 * @return {Boolean}
 */
function isPointInFace(point, face) {
  if ( isPointEdgeOfTheFace(point, face) ) {
    return false;
  }

  const [x, y] = point;
  const polygon = new Polygon();
  polygon.setCoordinates([[...face, face[0]]])
  return polygon.containsXY(x, y);
}

export function deepClone(obj) {
  if (obj === undefined) {
    return undefined;
  }
  var newObj = obj.constructor === Array ? [] : {};
  if (typeof obj !== 'object') {
    return
  } else {
    for (var i in obj) {
      if (obj.hasOwnProperty(i)) {
        newObj[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i];
      }
    }
  }
  return newObj
}
